//
//  SFCFileSetUpHelper.m
//  SVNForCocoa
//
//  Created by Jeremy Pereira on 11/11/2013.
//  Copyright (c) 2013 Jeremy Pereira. All rights reserved.
//

#import "SFCFileSetUpHelper.h"
#import "SFCLib+Internals.h"

static NSString* const SVN_ADMIN = @"/opt/local/bin/svnadmin";
NSString* const SVN_CMD = @"/opt/local/bin/svn";
static NSString* const SVN_SERVE = @"/opt/local/bin/svnserve";

@interface SFCFileSetUpHelper ()

@property (nonatomic, readonly, copy) NSString* workingPath;
@property (nonatomic, readonly, copy) NSString* myWorkingCopy;
@property (nonatomic, strong) NSTask* serverTask;

@end

@implementation SFCFileSetUpHelper

@synthesize rootPath = _rootPath;
@synthesize serverTask = _serverTask;

-(id) initWithFolder: (NSString*) folderPath
{
    self = [super init];
    if (self != nil)
    {
        _rootPath = [folderPath stringByAppendingPathExtension: @"testFiles"];
    }
    return self;
}

-(void) resetFiles
{
    NSFileManager* fileManager = [NSFileManager defaultManager];
    NSError* error = nil;
    if ([fileManager fileExistsAtPath: [self rootPath]])
    {
        if (![fileManager removeItemAtPath: [self rootPath] error: &error])
        {
			@throw [NSException exceptionWithName: @"SFCLibTest"
                                           reason: [error description]
                                         userInfo: nil];
        }
    }
    if (![fileManager createDirectoryAtPath: [self rootPath]
                withIntermediateDirectories: YES
                                 attributes: nil
                                      error: &error])
    {
        @throw [NSException exceptionWithName: @"SFCLibTest"
                                       reason: [error description]
                                     userInfo: nil];
    }
    [self runTask: SVN_ADMIN withArguments: @[@"create", [self repoPath]]];
    if (![fileManager createDirectoryAtPath: [self workingPath]
                withIntermediateDirectories: YES
                                 attributes: nil
                                      error: &error])
    {
        @throw [NSException exceptionWithName: @"SFCLibTest"
                                       reason: [error description]
                                     userInfo: nil];
    }
    [self runTask: SVN_CMD withArguments: @[@"checkout", [[self repoURI] absoluteString] , [self myWorkingCopy]]];
    NSLog(@"Current directory = %@", [fileManager currentDirectoryPath]);
    NSLog(@"Repo dir = %@", [self repoPath]);
}

-(NSString*) workingPath
{
    return [[self rootPath] stringByAppendingPathComponent: @".fsu"];
}

-(NSString*) myWorkingCopy
{
    return [[self workingPath] stringByAppendingPathComponent: @"wc"];
}

-(NSString*) repoPath
{
    return [[self rootPath] stringByAppendingPathComponent: @"repo"];
}

-(NSString*) wcPath
{
    return [[self rootPath] stringByAppendingPathComponent: @"wc"];
}

-(NSURL*) repoURI
{
    NSString* absoluteRepoPath = [[self repoPath] absolutePath];
    NSURL* repoURL = [NSURL fileURLWithPath: absoluteRepoPath];
    return [repoURL URLByStandardizingPath];
}

-(NSURL*) svnServeURI
{
    return [NSURL URLWithString: @"svn://localhost/"];
}

-(void) writeRepoConfig: (NSString*) configContent
{
	NSString* repoConfDir = [[self repoPath] stringByAppendingPathComponent: @"conf"];
    NSString* configFile = [repoConfDir stringByAppendingPathComponent: @"svnserve.conf"];
    NSData* configData = [configContent dataUsingEncoding: NSUTF8StringEncoding];
    [configData writeToFile: configFile atomically: NO];
}

-(void) writePasswords: (NSString*) passwords
{
	NSString* repoConfDir = [[self repoPath] stringByAppendingPathComponent: @"conf"];
    NSString* passwordFile = [repoConfDir stringByAppendingPathComponent: @"passwd"];
    NSData* passwordData = [passwords dataUsingEncoding: NSUTF8StringEncoding];
    [passwordData writeToFile: passwordFile atomically: NO];
}

-(void) addFileToRepository: (NSString*) fileName content: (NSString*) content
{
    NSFileManager* fileManager = [NSFileManager defaultManager];
    NSString* savedWD = [fileManager currentDirectoryPath];
    [fileManager changeCurrentDirectoryPath: [self myWorkingCopy]];
    @try
    {
        [[content dataUsingEncoding: NSUTF8StringEncoding] writeToFile: fileName
                                                            atomically: NO];
        [self runTask: SVN_CMD withArguments: @[@"add", fileName]];
        [self runTask: SVN_CMD withArguments: @[@"commit", @"-m", @"adding file", fileName]];
    }
    @finally
    {
        [fileManager changeCurrentDirectoryPath: savedWD];
    }
}

-(void) updateFileInRepository: (NSString*) fileName
                 appendContent: (NSString*) content
{
    NSFileManager* fileManager = [NSFileManager defaultManager];
    NSString* savedWD = [fileManager currentDirectoryPath];
    [fileManager changeCurrentDirectoryPath: [self myWorkingCopy]];
    @try
    {
        [self runTask: SVN_CMD withArguments: @[ @"update"]];
        BOOL isDirectory = NO;
        bool fileExists = [fileManager fileExistsAtPath: fileName
                                            isDirectory: &isDirectory];
		if (!fileExists || isDirectory)
        {
            NSString* reason = [NSString stringWithFormat: @"%@ does not exist or is a directory", fileName];
            @throw [NSException exceptionWithName: [self className]
                                           reason: reason
                                         userInfo: nil];
        }
        NSFileHandle* fileHandle = [NSFileHandle fileHandleForUpdatingAtPath: fileName];
        if (fileHandle == nil)
        {
            NSString* reason = [NSString stringWithFormat: @"Could not open %@ to append content", fileName];
            @throw [NSException exceptionWithName: [self className]
                                           reason: reason
                                         userInfo: nil];
        }
        [fileHandle seekToEndOfFile];
        [fileHandle writeData: [content dataUsingEncoding: NSUTF8StringEncoding]];
        [self runTask: SVN_CMD withArguments: @[ @"commit", @"-m", @"updating file", fileName]];
    }
    @finally
    {
        [fileManager changeCurrentDirectoryPath: savedWD];
    }
}


-(void) runTask: (NSString*) taskPath withArguments: (NSArray*) arguments
{
	NSTask* theTask = [NSTask launchedTaskWithLaunchPath: taskPath
                                               arguments: arguments];
    [theTask waitUntilExit];
	int status = [theTask terminationStatus];
    if (status != 0)
    {
        @throw [NSException exceptionWithName: [self className]
                                       reason: [NSString stringWithFormat:
                                                @"%@ exited with status %d",
                                                taskPath,
                                                status]
                                     userInfo: nil];
    }
}

-(NSString*) outputOfTask: (NSString*) taskPath
            withArguments:(NSArray *)arguments
{
    NSTask* task = [[NSTask alloc] init];
    [task setLaunchPath: taskPath];
    [task setArguments:arguments];
    NSPipe* outputPipe = [NSPipe pipe];
    [task setStandardOutput: outputPipe];
	[task launch];
    NSFileHandle* outputHandle = [outputPipe fileHandleForReading];
    NSData* theData = [outputHandle readDataToEndOfFile];
    [task waitUntilExit];
	int status = [task terminationStatus];
    NSString* outputString = nil;
    if (status == 0)
    {
        outputString = [[NSString alloc] initWithData: theData
                                             encoding: NSUTF8StringEncoding];
    }
    return outputString;

}

-(bool) file: (NSString*) filePath statusIs: (NSString*) svnStatus
{
    NSString* outputString = [self outputOfTask: SVN_CMD
                                  withArguments: @[ @"status", @"-v", filePath]];
    bool ret = false;
    if (outputString != nil)
    {
        ret = [outputString hasPrefix: svnStatus];
    }
    return ret;
}

-(size_t) sizeOfFile: (NSString*) file
{
	NSFileManager* fm = [NSFileManager defaultManager];
    NSDictionary* attributes = [fm attributesOfItemAtPath: file error: nil];
    size_t ret = 0;
    if (attributes != nil)
    {
        ret = [[attributes objectForKey: NSFileSize] unsignedLongValue];
    }
    return ret;
}


-(void) checkoutWc
{
    [self runTask: SVN_CMD
    withArguments: @[@"checkout", [[self repoURI] absoluteString], [self wcPath]]];
}

-(void) startServer
{
    assert([self serverTask] == nil);

	NSTask* theTask = [NSTask launchedTaskWithLaunchPath: SVN_SERVE
                                               arguments: @[ @"-d",
                                                             @"--foreground",
                                                             @"--threads",
                                                             @"-r", [self repoPath]]];
    if (theTask == nil)
    {
        @throw [NSException exceptionWithName: [self className]
                                       reason: @"Failed to create the sever task"
                                     userInfo: nil];
    }
    [NSThread sleepForTimeInterval: 0.5];	//  Give the server a chance to start
    if (![theTask isRunning])
    {
        @throw [NSException exceptionWithName: [self className]
                                       reason: @"The server task has terminated prematurely"
                                     userInfo: nil];
    }
    [self setServerTask: theTask];
}

-(void) stopServer
{
    NSTask* theTask = [self serverTask];
    if (theTask != nil)
    {
        [theTask terminate];
        [theTask waitUntilExit];
        [self setServerTask: nil];
    }
}

-(void) append: (NSString*) content toFileInWc: (NSString*) fileName
{
    NSFileManager* fileManager = [NSFileManager defaultManager];
    NSString* savedWD = [fileManager currentDirectoryPath];
    [fileManager changeCurrentDirectoryPath: [self wcPath]];
    @try
    {
        [self append: content toFile: fileName];
    }
    @finally
    {
        [fileManager changeCurrentDirectoryPath: savedWD];
    }
}

-(void) append: (NSString*) content toFile: (NSString*) fileName
{
    NSFileManager* fileManager = [NSFileManager defaultManager];
    BOOL isDirectory = NO;
    bool fileExists = [fileManager fileExistsAtPath: fileName
                                        isDirectory: &isDirectory];
    if (!fileExists || isDirectory)
    {
        NSString* reason = [NSString stringWithFormat: @"%@ does not exist or is a directory", fileName];
        @throw [NSException exceptionWithName: [self className]
                                       reason: reason
                                     userInfo: nil];
    }
    NSFileHandle* fileHandle = [NSFileHandle fileHandleForUpdatingAtPath: fileName];
    if (fileHandle == nil)
    {
        NSString* reason = [NSString stringWithFormat: @"Could not open %@ to append content", fileName];
        @throw [NSException exceptionWithName: [self className]
                                       reason: reason
                                     userInfo: nil];
    }
    [fileHandle seekToEndOfFile];
    [fileHandle writeData: [content dataUsingEncoding: NSUTF8StringEncoding]];
}


-(void) touch: (NSString*) aFile
{
    NSFileManager* fm = [NSFileManager defaultManager];
    if (![fm fileExistsAtPath: aFile isDirectory: nil])
    {
        [fm createFileAtPath: aFile contents: [NSData data] attributes: nil];
    }
}


@end
